#include "mainwindow.h"
#include <QTimer>
#include <QMainWindow>
#include <QLabel>
#include <QPalette>
#include <QKeyEvent>
#include <QMessageBox>
#include <fstream>
#include <string>

#include "dialog.h"
#include "ui_dialog.h"

#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    configureBackground();
    configureScore();
    ui->setupUi(this);
}

void MainWindow::configureBackground()
{
    QPixmap backg(":/images/Decorations/background.png");

    //Setup Label for black background
    background = new QLabel(this);
    background->setPixmap(backg);
    background->setGeometry(0,0,800,600);
    background->setVisible(false);
}

//Set up label for score
void MainWindow::configureScore()
{
    score = new QLabel(this);
    score->setGeometry(715,0, 100, 66);
    score->setStyleSheet("QLabel { background-color : black; color : yellow; }");
    score->setVisible(false);

}

//Destructor
MainWindow::~MainWindow()
{
    for (size_t i = 0; i < pixmaps.size(); ++i)
    {
        delete pixmaps.at(i);
    }
    delete ui;
}

//Hides all widgets on main screen
void MainWindow::hideMain()
{
    ui->btnNewGame->setVisible(false);
    ui->centralWidget->setVisible(false);
    ui->frmButtons->setVisible(false);
    ui->frmCutScene->setVisible(false);
    ui->lblGameLogo->setVisible(false);
    ui->lblTeaser->setVisible(false);
    ui->statusBar->setVisible(false);

}

void MainWindow::on_btnNewGame_clicked()
{
    //get player name
    playerName = ui->lnUserName->text();

    //pop up a message if the player did not enter a name
    if (playerName == "")
    {
        QMessageBox::warning(this, "error", "please enter your name");
        return;
    }

    //get game difficulty (number of walls)
    if (ui->lnDifficulty->text() != "" && isdigit(ui->lnDifficulty->text().toStdString().at(0)))
    {
        difficulty =  stoi((ui->lnDifficulty->text().toStdString()));

        //max difficulty is 10
        if (difficulty < 1)
        {
            difficulty = 1;
        }
        else if (difficulty > 29)
        {
            difficulty = 29;
        }
    }
    else
    {
        //default difficulty is 1 if user entered an invalid difficultly
        difficulty = 1;
    }

    //Instantiate new Game, show Background and Score labels,
    game = new Game();
    game->createWorld(difficulty);
    hideMain();
    background->show();

    gameRunning = true;

    wipe();
    buildQLabelQPixmap();

    //display the score
    score->show();

    //Update at roughly 30 FPS
    timer = new QTimer(this);
    timer->setInterval(33);
    connect(timer, SIGNAL(timeout()), this, SLOT(timerHit()));
    timer->start();

}


void MainWindow::wipe()
{
    for (size_t i = 0; i < labels.size(); ++i)
    {
        delete labels.at(i);
        delete pixmaps.at(i);
    }
    labels.clear();
    pixmaps.clear();
}

//starts or stops the timer based on whether the game is running or not
void MainWindow::toggleRunning()
{
    if (gameRunning)
    {
        timer->start();
    }
    else
    {
        timer->stop();
    }
}


void MainWindow::highScores(bool gameOver)
{
    //read the old high scores from a file
    ifstream infile("score.txt");
    string curLine;
    vector<string> players;
    bool playerScoreInserted = false;
    for (size_t j = 0; j < 10; j++)
    {
        getline(infile, curLine);
        double tmpScore = stod(curLine);
        if(game->getScore() > tmpScore && !playerScoreInserted && gameOver)
        {
            //position the player's high score in the list (if it is a high score...)
            QString playerScore = QString::fromStdString(to_string(game->getScore())) + "     " + playerName;
            players.push_back(playerScore.toStdString());
            playerScoreInserted = true;
        }
        players.push_back(curLine);
    }
    infile.close();

    //save the top 10 high scores to a text file
    ofstream outfile("score.txt");
    for (size_t i = 0; i < 10; i++)
    {
        outfile << players.at(i) << endl;
    }
    outfile.close();

    //display a dialog with the high scores
    Dialog dialog;
    dialog.show();
    dialog.setWindowTitle("HIGH SCORES");
    for (size_t j = 0; j < 10; j++)
    {
        dialog.ui->lstScores->addItem(QString::fromStdString(players.at(j)));
    }
    dialog.ui->lblPlayerScore->setText(QString::fromStdString("Your Score: " + to_string(game->getScore())));
    if (dialog.exec() == QDialog::Accepted) {}
}

void MainWindow::timerHit()
{
    //Stop game if lives run out
    update();
    if (game->lives == 0 || gameRunning == false)
    {
        timer->stop();
        highScores(true);
        gameRunning = false;
    }

    //figure out how many dots are left
    int numDotsLeft = 0;
    for (int i = 0; i < game->getObjectVectorSize(); i++) {
        Dot *d = dynamic_cast<Dot*>(game->getObjectByID(i));
        if (d != 0){
            if (!d->isEaten())
                numDotsLeft++;
        }
    }

    //if enough dots are gone, begin a new level
    bool gameOver = false;
    if (numDotsLeft < 10)
    {
        startNewLevel();
    }

    if (!gameOver)
    {
        game->move();

        updateLabelPositions();

        score->setText("<b>Score:</b><br> " + QString::fromStdString(to_string(game->getScore())) +
                       "<br><b>Lives:</b><br> " + QString::fromStdString(to_string(game->getLives())));
        score->update();
    }
}

void MainWindow::updateLabelPositions()
{
    //Update label positions
    for (size_t i = 0; i < labels.size(); ++i)
    {
        if (game->getObjectByID(i)->getType() != "0Wall" && game->getObjectByID(i)->getType() != "1Wall" &&
                game->getObjectByID(i)->getType() != "Dot")
        {

            QLabel &lbl = *labels.at(i);
            QPixmap &pxmp = *pixmaps.at(i);

            pxmp = QString::fromStdString(game->getObjectByID(i)->getImage());
            lbl.setPixmap(pxmp);
            if (game->getObjectByID(i)->getType() == "PacMan")
            {
                QPixmap &p = *pixmaps.at(i);
                p = QString::fromStdString(game->getObjectByID(i)->getImage());
            }
            lbl.move(game->getObjectByID(i)->getX(), game->getObjectByID(i)->getY());
            lbl.update();
        }
        else if (game->getObjectByID(i)->getType() == "Dot")
        {
            Dot *dot = dynamic_cast<Dot*>(game->getObjectByID(i));
            if (dot->IsEaten())
            {
                dynamic_cast<QLabel*>(labels.at(i))->setVisible(false);
                dynamic_cast<QLabel*>(labels.at(i))->update();
            }
        }
    }
    score->setText("<b>Score:</b><br>" + QString::fromStdString(to_string(game->getScore())) +
                   "<br><b>Lives:</b><br>" + QString::fromStdString(to_string(game->getLives())));
    score->update();

}

//performs the appropriate action based on keyboard presses
void MainWindow::keyPressEvent(QKeyEvent *kEvent)
{
    //move paddle based on 'q' and 'a' keys
    QString key = kEvent->text();
    if (key == "q") //move left paddle up
    {
        game->movePaddle(1, -1);
    }
    else if (key == "a") //move left paddle down
    {
        game->movePaddle(1, 1);
    }
    else if (key == "p") //move right paddle up
    {
        game->movePaddle(2, -1);
    }
    else if (key == "l") //move right paddle down
    {
        game->movePaddle(2, 1);
    }
    else if (key == "t") //pause or unpause the game
    {
        toggleGameRunning();
        toggleRunning();
    }
    else if (key == "g") //add a spare life...
    {
        game->lives++;
    }
    else if (key == "b") //save the game
    {
        game->saveGame(difficulty);
    }
    else if (key == "c") //remove a dot
    {
        removeDots();
    }
    else if (key == "n") //advance a level
    {
        startNewLevel();
    }
}

//display current high scores
void MainWindow::on_btnHighScores_clicked()
{
    //show the high scores dialog
    Dialog dial;
    dial.show();
    dial.setWindowTitle("HIGH SCORES");

    ifstream infile("score.txt");
    string curLine;

    //read the high scores from the score.txt file
    for (size_t j = 0; j < 10; j++)
    {
        getline(infile, curLine);
        dial.ui->lstScores->addItem(QString::fromStdString(curLine));
    }
    infile.close();

    if (dial.exec() == QDialog::Accepted) {}
}

//loads the last saved game
void MainWindow::on_btnLoad_clicked()
{
    //Instantiate new Game, show Background and Score labels,
    playerName = ui->lnUserName->text();
    if (playerName == "")
    {
        QMessageBox::warning(this, "error", "please enter your name");
        return;
    }

    game = new Game();
    game->loadGame();

    hideMain();
    background->show();

    gameRunning = true;

    buildQLabelQPixmap();

    score->show();

    //Update at roughly 30 FPS
    timer = new QTimer(this);
    timer->setInterval(33);
    connect(timer, SIGNAL(timeout()), this, SLOT(timerHit()));
    timer->start();
}

void MainWindow::buildQLabelQPixmap()
{
    labels.clear();
    pixmaps.clear();

    //Build vectors of QLabels and QPixmaps
    for (int i = 0; i < game->getObjectVectorSize(); ++i)
    {
        labels.push_back(new QLabel(this));
        pixmaps.push_back(new QPixmap());

        QLabel &lbl = *labels.at(i);
        QPixmap &pxmp = *pixmaps.at(i);

        pxmp = QString::fromStdString(game->getObjectByID(i)->getImage());
        lbl.setPixmap(pxmp);
        lbl.setGeometry(game->getObjectByID(i)->getX(), game->getObjectByID(i)->getY(), pxmp.width(), pxmp.height());
        lbl.show();
    }
}

void MainWindow::startNewLevel()
{
    //increase the difficulty
    difficulty++;

    //
    if (difficulty < 31)
    {
        timer->stop();
        highScores(false);
        timer->start();

        //create a new game. carry over the old values of score and lives
        int score = game->getScore();
        int oldLives = game->getLives();
        delete game;
        wipe();
        game = new Game();
        game->createWorld(difficulty);
        game->setScore(score);
        game->setLives(oldLives);
        buildQLabelQPixmap();
    }
    else //you have finished the last level and reached the end of the game. congratulations!
    {
        timer->stop();
        highScores(true);
    }
}

//instantly eat all the remaining dots on the screen
void MainWindow::removeDots()
{
    bool done = false;
    for (int i = 0; i < game->getObjectVectorSize(); i++) {

        Dot *d = dynamic_cast<Dot*>(game->getObjectByID(i));
        if (d != 0)
        {
            if (!d->isEaten())
                game->eat(game->getObjectByID(0), game->getObjectByID(i));
                game->incrementScore(10);
                done = true;
        }
    }
}
